﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Xml.Linq;
using LightCAD.Core.Element3d;
using LightCAD.MathLib;
using static System.Collections.Specialized.BitVector32;

namespace LightCAD.Core
{
    /// <summary>
    /// 组件实例引用
    /// </summary>
    public class LcComponentInstance : LcElement,IComponentInstance
    {
        public Vector3 Position => Transform3d.Position;
        public Euler Rotation => Transform3d.Euler;
        public Transform3d Transform3d { get;private set; }
       
        public LcComponentDefinition Definition { get;protected set; }

        public List<AssociatePoint> AssociatePoints { get; }=new List<AssociatePoint>();

        public List<AssociateElement> AssociateElements { get; } =new List<AssociateElement>();

        public string MaterialName { get; set; }
        public int MaterialId { get; set; } = 0;
        public LcMaterial Material { get; set; }
        public LcParameterSet Parameters { get; set; }

        private Solid3dCollection solids;
        public Solid3dCollection Solids
        {
            get
            {
                if (solids == null)
                {
                    solids = this.Definition.Solid3dProvider.GetSolid3dsFunc(this.Parameters);
                }
                return solids;
            }
        }
        private Curve2dGroupCollection curves;
        public Curve2dGroupCollection Curves
        {
            get
            {
                if (curves == null)
                {
                    bool useProjection = false;
                    curves = this.Definition.Curve2dProvider.GetCurve2dsFunc(this.Parameters);
                    if (curves == null)
                    {
                        var curveGrps = new Curve2dGroupCollection();
                        foreach (var solid in this.Solids)
                        {
                            if (solid.Geometry == null)
                                return null;

                            curveGrps.Add(new Curve2dGroup()
                            {
                                Curve2ds = Projection.GetProjectionResult(solid.Geometry)
                            });
                        }
                        useProjection = true;
                        curves = curveGrps;
                    }
                    if (!useProjection)
                    {
                        foreach (var grp in curves)
                        {
                            foreach (var c2 in grp.Curve2ds)
                            {
                                c2.RotateAround(new Vector2(), Transform3d.Euler.Z);
                                c2.Translate(Transform3d.Position.X, Transform3d.Position.Y);
                            }

                        }
                    }
                }
                return curves;
            }
        }
        private object _action;
        public object Rt3DAction
        {
            get
            {
                if (_action == null)
                {
                    LcDocument.Element3dActions.TryGetValue(this.Type, out _action);
                }
                if (_action == null)
                {
                    throw new Exception($"Can't find Action. (ElementType={this.Type})");
                }
                return _action;
            }
        }

        public LcComponentInstance(LcComponentDefinition definition)
        {
            this.Definition = definition;
            this.Transform3d = new Transform3d(Definition.Origin);
            this.Parameters=new LcParameterSet(definition.Parameters);
        }

        public override LcElement Clone()
        {
            var clone = new LcComponentInstance(Definition);
            clone.Copy(this);
            return clone;
        }

        public override void Copy(LcElement src)
        {
            var comref = ((LcComponentInstance)src);
            this.Parameters = comref.Parameters.Clone();
            this.Definition = comref.Definition;
            this.Transform3d = comref.Transform3d.Clone();
            this.AssociatePoints.AddRange(comref.AssociatePoints.Clone());
            this.AssociateElements.AddRange(comref.AssociateElements.Clone());
        }

        public virtual void Invoke(string methodName, InvokeToken token)
        {
        }

        private ListEx<Vector3> snap3dPoints=null;
        public ListEx<Vector3> Snap3dPoints
        {
            get
            {
                if (snap3dPoints == null)
                    snap3dPoints = GetSnap3dPoint();
                return snap3dPoints;
            }
        }
        public LcComponentInstance ResetSnap3dPoints()
        {
            this.snap3dPoints = null;
            return this;
        }
        protected virtual ListEx<Vector3> GetSnap3dPoint()
        {
            //动态捕捉的点默认从边里面取
            var points = new ListEx<Vector3>();
            for (int i = 0; i < this.Solids.Count; i++)
            {
                var posArray = this.Solids[i].Edge.Verteics;
                for (int j = 0; j < posArray.Length; j += 3)
                {
                    points.PushNoRepeat(new Vector3(posArray[j], posArray[j + 1], posArray[j + 2]));
                }
            }
         
            return points;
        }
        public LcComponentInstance UpdateTransformMatrix() 
        {
            this.OnPropertyChangedAfter(nameof(Transform3d), Transform3d, Transform3d);
            this.Transform3d.Update();
            this.Reset();
            this.OnPropertyChangedAfter(nameof(Transform3d), Transform3d, Transform3d);
            return this;
        }


        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            base.WriteProperties(writer, soptions);

            if (this.Definition != null)
            {
                writer.WriteStringProperty("ComponentId", this.Definition.Uuid);
            }
           // writer.WriteCollectionProperty<Curve2d>(nameof(ShapeCurves), this.ShapeCurves, soptions, (w, c) => w.WriteCurve2dObject(c, soptions));
        }
        public override void ReadProperties(ref JsonElement jele)
        {
            base.ReadProperties(ref jele);

            if (jele.TryGetProperty("ComponentId", out JsonElement _))
            {
                this.Definition = this.Document.Components[jele.ReadStringProperty("ComponentId")];
            }
        }
        public override void Scale(Vector2 basePoint, double scaleFactor)
        {
            Matrix3 scaleMatrix = Matrix3.GetScale(scaleFactor, basePoint);
            Reset();
        }
        public override void Rotate(Vector2 basePoint, double rotateAngle)
        {
            Matrix3 rotateMatrix = Matrix3.RotateInRadian(rotateAngle, basePoint);
            Reset();
        }
        public override void Mirror(Vector2 axisStart, Vector2 axisEnd)
        {
            Matrix3 mirrorMatrix = Matrix3.GetMirror(axisEnd, axisStart);
            Reset();
        }
        public override void Move(Vector2 startPoint, Vector2 endPoint)
        {
            this.Transform3d.Position.X += endPoint.X - startPoint.X;
            this.Transform3d.Position.Y += endPoint.Y - startPoint.Y;
            this.UpdateTransformMatrix();
            Reset();
        }
        public override void Translate(double dx, double dy)
        {

            this.Transform3d.Position.X += dx;
            this.Transform3d.Position.Y += dy;
            this.UpdateTransformMatrix();
            Reset();
        }
        public void Reset()
        {
            this.curves = null;
            this.solids = null;
            ResetBoundingBox();
        }
        public override Box2 GetBoundingBox()
        {
            var box = new Box2();
            if (this.Curves == null)
            {
                return null;
            }
            box.SetFromPoints( this.Curves.SelectMany(cs => cs.Curve2ds.SelectMany(c=>c.GetPoints())).ToArray());
            return box;
        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {
            var box = new Box2();
            box.SetFromPoints();
            return box;
        }
        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                //如果元素盒子，与多边形盒子不相交，那就可能不相交
                return false;
            }
            var points = this.Curves.SelectMany(cs => cs.Curve2ds.SelectMany(c => c.GetPoints())).ToArray();
            for (int i = 0; i < points.Length-1; i++)
            {
                if (GeoUtils.IsPolygonIntersectLine(testPoly.Points, points[i], points[i + 1]))
                    return true;
            }
            return false;
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            var points = this.Curves.SelectMany(cs => cs.Curve2ds.SelectMany(c => c.GetPoints())).ToArray();
            for (int i = 0; i < points.Length - 1; i++)
            {
                if (GeoUtils.IsPolygonContainsLine(testPoly.Points, points[i], points[i + 1]))
                    return true;
            }
            return false;
        }
    }
}
